/****************************************************************************
 *  _____       ______  _____
 * |_   _|     |  ____|/ ____|
 *   | |  _ __ | |__  | (___    Institute of Embedded Systems
 *   | | | '_ \|  __|  \___ \   Zürcher Hochschule für Angewandte
 *  _| |_| | | | |____ ____) |  Wissenschaften
 * |_____|_| |_|______|_____/   8401 Winterthur, Switzerland
 *
 ****************************************************************************
 *
 * @version $Rev: 483 $
 * @author  $Author: bacr $
 * @date    $Date: 2014-07-10 16:00:27 +0200 (jeu., 10 juil. 2014) $
 *
 ***************************************************************************/

#ifndef __PTP2_LOG_H__
#define __PTP2_LOG_H__

#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>

#include "hcpp_config.h"
//#include "hcpp/ptp2_types.h"
//#include "hcpp/ptp2_arp_table.h"
//#include "packet/packet.h"

/*** DEFINES ****************************************************************/

#define PTP2_LOG_STREAM                     stderr
#define PTP2_LOG_PRINTF                     fprintf
#define PTP2_LOG_VPRINTF                    vfprintf
#define PTP2_LOG_FLUSH                      fflush
#define PTP2_LOG_TIME_MSEC_HEX              0

                                            /* bit + space + NUL-character */
#define PTP2_LOG_BIT8_LEN                   8  + 1 + 1
#define PTP2_LOG_BIT16_LEN                  16 + 3 + 1
#define PTP2_LOG_BIT32_LEN                  32 + 7 + 1

                                            /* days + space + time + NUL-character */
#define PTP2_LOG_TIME_LEN                   5 + 1 + 8 + 1

#define PTP2_LOG_ADDRESS_LEN                PTP2_LOG_IPV6_LEN
#define PTP2_LOG_MAC_LEN                    17 + 1    /* MAC-address string length, with NUL-character */
#define PTP2_LOG_IPV4_LEN                   15 + 1    /* IPv4-address string length, with NUL-character */
#define PTP2_LOG_IPV6_LEN                   41 + 1    /* IPv6-address string length, with NUL-character */
#define PTP2_LOG_CLOCK_IDENTITY_LEN         23 + 1    /* clock identity string length, with NUL-character */
#define PTP2_LOG_EUI48_LEN                  17 + 1    /* EUI48 string length, with NUL-character */

/*** MACROS *****************************************************************/

extern const char    *ptp2_log_empty_str;

#define PTP2_LOG_HEADER_CATEGORY(category)           PTP2_LOG_PRINTF(PTP2_LOG_STREAM, "%s", PTP2_LOG_CATEGORY_STRING[category]);
#define PTP2_LOG_HEADER_LEVEL(level)                 PTP2_LOG_PRINTF(PTP2_LOG_STREAM, "%s", PTP2_LOG_LEVEL_STRING[level]);

#define PTP2_LOG_FUNCTION(with_header, function, category, level, msg) \
    do { \
        if (ptp2_log && level <= PTP2_LOG_CATEGORY_LEVEL[category]) { \
            if (with_header) { \
                ptp2_log_print_header(category, level); \
            } \
            function msg; \
        } \
    } while(0)

#define PTP2_LOG_PACKET_FUNCTION(function, category, level, packet, msg) \
    do { \
        if (ptp2_log && level <= PTP2_LOG_CATEGORY_LEVEL[category]) { \
            ptp2_log_print_header(category, level); \
            ptp2_log_println msg; \
            function packet; \
        } \
    } while(0)

#define PTP2_LOG_CHAR_STREAM(category, level, stream, len, msg) \
    do { \
        if (ptp2_log && level <= PTP2_LOG_CATEGORY_LEVEL[category]) { \
            ptp2_log_print_header(category, level); \
            ptp2_log_print msg; \
            ptp2_log_char_stream(stream, len); \
        } \
    } while(0)

#define PTP2_LOG_ENABLE(category, level)                                (ptp2_log && level <= PTP2_LOG_CATEGORY_LEVEL[category])

#define PTP2_LOG_PRINT(category, level, msg)                            PTP2_LOG_FUNCTION(1, ptp2_log_print,       category, level, msg)
#define PTP2_LOG_PRINTLN(category, level, msg)                          PTP2_LOG_FUNCTION(1, ptp2_log_println,     category, level, msg)
#define PTP2_LOG_APPEND(category, level, msg)                           PTP2_LOG_FUNCTION(0, ptp2_log_append,      category, level, msg)
#define PTP2_LOG_APPENDLN(category, level, msg)                         PTP2_LOG_FUNCTION(0, ptp2_log_appendln,    category, level, msg)

#define PTP2_LOG_RAW_PACKET(category, level, packet, msg)               PTP2_LOG_PACKET_FUNCTION(ptp2_log_raw_packet,               category, level, packet, msg)
#define PTP2_LOG_PACKET(category, level, packet, msg)                   PTP2_LOG_PACKET_FUNCTION(ptp2_log_packet,                   category, level, packet, msg)
#define PTP2_LOG_ETHERNET_PACKET(category, level, packet, msg)          PTP2_LOG_PACKET_FUNCTION(ptp2_log_ethernet_packet,          category, level, packet, msg)
#define PTP2_LOG_ARP_PACKET(category, level, packet, msg)               PTP2_LOG_PACKET_FUNCTION(ptp2_log_arp_packet,               category, level, packet, msg)
#define PTP2_LOG_IPV4_PACKET(category, level, packet, msg)              PTP2_LOG_PACKET_FUNCTION(ptp2_log_ipv4_header,              category, level, packet, msg)
#define PTP2_LOG_IPV6_PACKET(category, level, packet, msg)              PTP2_LOG_PACKET_FUNCTION(ptp2_log_ipv6_header,              category, level, packet, msg)
#define PTP2_LOG_UDP_PACKET(category, level, packet, msg)               PTP2_LOG_PACKET_FUNCTION(ptp2_log_udp_header,               category, level, packet, msg)
#define PTP2_LOG_PTP2_PACKET(category, level, packet, msg)              PTP2_LOG_PACKET_FUNCTION(ptp2_log_ptp2_header,              category, level, packet, msg)
#define PTP2_LOG_PTP2_SIGNALING_PACKET(category, level, packet, msg)    PTP2_LOG_PACKET_FUNCTION(ptp2_log_ptp2_signaling_header,    category, level, packet, msg)
#define PTP2_LOG_PTP2_ANNOUNCE_PACKET(category, level, packet, msg)     PTP2_LOG_PACKET_FUNCTION(ptp2_log_ptp2_announce_header,     category, level, packet, msg)

/*** converts a defined type into a bit field string ***/
#define PTP2_LOG_BIT8(var, mask, var_str)                   uint8_t var_str[PTP2_LOG_BIT8_LEN];             ptp2_log_bit8 (var, mask, var_str)
#define PTP2_LOG_BIT16(var, mask, var_str)                  uint8_t var_str[PTP2_LOG_BIT16_LEN];            ptp2_log_bit16(var, mask, var_str)
#define PTP2_LOG_BIT32(var, mask, var_str)                  uint8_t var_str[PTP2_LOG_BIT32_LEN];            ptp2_log_bit32(var, mask, var_str)

/*** converts a defined type into a string ***/

#define PTP2_LOG_TIME(var, var_str)                         uint8_t var_str[PTP2_LOG_TIME_LEN];             ptp2_log_time(var, var_str)

/* node address */
#define PTP2_LOG_ADDRESS(var, var_str)                      uint8_t var_str[PTP2_LOG_ADDRESS_LEN]; \
                                                            do { \
                                                                switch (var->type) { \
                                                                    case PTP2_ADDRESS_LAYER2: ptp2_log_mac (&(var->mac),  var_str); break; \
                                                                    case PTP2_ADDRESS_IPV4:   ptp2_log_ipv4(&(var->ipv4), var_str); break; \
                                                                    case PTP2_ADDRESS_IPV6:   ptp2_log_ipv6(&(var->ipv6), var_str); break; \
                                                                    default:                  var_str[0] = '\0';                    break; \
                                                                } \
                                                            } while(0)
/* net address */
#define PTP2_LOG_MAC(var, var_str)                          uint8_t var_str[PTP2_LOG_MAC_LEN];              ptp2_log_mac(var, var_str)
#define PTP2_LOG_IPV4(var, var_str)                         uint8_t var_str[PTP2_LOG_IPV4_LEN];             ptp2_log_ipv4(var, var_str)
#define PTP2_LOG_IPV6(var, var_str)                         uint8_t var_str[PTP2_LOG_IPV6_LEN];             ptp2_log_ipv6(var, var_str)
#define PTP2_LOG_CLOCK_IDENTITY(var, var_str)               uint8_t var_str[PTP2_LOG_CLOCK_IDENTITY_LEN];   ptp2_log_clock_identity(var, var_str)
#define PTP2_LOG_EUI48(var, var_str)                        uint8_t var_str[PTP2_LOG_EUI48_LEN];            ptp2_log_eui48(var, var_str)

/*** DECLARATION ************************************************************/

/* IMPORTANT: not only add a new category but
 *            define strings in ptp2_log.c and
 *            define boot-time levels in hcpp_config.h */
typedef enum {
    PTP2_LOG_MASTER,
    PTP2_LOG_LOG,
    PTP2_LOG_HAL_ENET,
    PTP2_LOG_HAL_TIMER,
    PTP2_LOG_HAL_PTP_TIME,
    PTP2_LOG_HAL_SLAVEMGM,
    PTP2_LOG_HAL_SYSCONF,
    PTP2_LOG_HAL_TOD,
    PTP2_LOG_HAL_ESMC,
    PTP2_LOG_FSM,
    PTP2_LOG_PORT,
    PTP2_LOG_TASK,
    PTP2_LOG_TASK_QUEUE,
    PTP2_LOG_PACKET_ETHERNET,
    PTP2_LOG_PACKET_ARP,
    PTP2_LOG_PACKET_IPV4,
    PTP2_LOG_PACKET_IPV6,
    PTP2_LOG_PACKET_ICMP,
    PTP2_LOG_PACKET_UDP,
    PTP2_LOG_PACKET_PTP2,
    PTP2_LOG_PACKET_PTP2_SIGNALING,
    PTP2_LOG_PACKET_PTP2_ANNOUNCE,
    PTP2_LOG_PACKET_ANALYZER,
    PTP2_LOG_PACKET_ASSEMBLER,
    PTP2_LOG_PING_TABLE,
    PTP2_LOG_ARP_TABLE,
    PTP2_LOG_NODE_TABLE,
    PTP2_LOG_SHADOW_TABLE,
    PTP2_LOG_PROFILE,
    PTP2_LOG_BMCA,
    PTP2_LOG_TOD,
    PTP2_LOG_DATASET_DEFAULT,
    PTP2_LOG_DATASET_CURRENT,
    PTP2_LOG_DATASET_PARENT,
    PTP2_LOG_DATASET_TIMEPROP,
    PTP2_LOG_DATASET_PORT,
    PTP2_LOG_DATASET_FOREIGN_MASTER,
    PTP2_LOG_SPI_MASTER,
    PTP2_LOG_SPI_SLAVE,
    PTP2_LOG_PPS_PLL,
    PTP2_LOG_COUNT
} ptp2_log_category_t;

typedef enum {
    PTP2_LOG_ERROR          = 0,
    PTP2_LOG_WARNING        = 1,
    PTP2_LOG_INFO           = 2,
    PTP2_LOG_DEBUG          = 3,
    PTP2_LOG_VERBOSE        = 4
} ptp2_log_level_t;

/*** DEFINITION *************************************************************/

extern ptp2_log_level_t         PTP2_LOG_CATEGORY_LEVEL[];
extern const char              *PTP2_LOG_CATEGORY_STRING[];
extern const char              *PTP2_LOG_LEVEL_STRING[];
extern bool                     ptp2_log;                /**< don't use it directly! use functions */

void ptp2_log_init();

static inline void ptp2_log_enable()  { ptp2_log = true;  }
static inline void ptp2_log_disable() { ptp2_log = false; }

/* messages */
void        ptp2_log_print_header           (ptp2_log_category_t category, ptp2_log_level_t level);
void        ptp2_log_print                  (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void        ptp2_log_println                (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void        ptp2_log_append                 (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void        ptp2_log_appendln               (const char *format, ...) __attribute__ ((format (printf, 1, 2)));

/* to bit field */
void        ptp2_log_bit8                   (const uint8_t      value,  const uint8_t   mask,   uint8_t *str);
void        ptp2_log_bit16                  (const uint16_t     value,  const uint16_t  mask,   uint8_t *str);
void        ptp2_log_bit32                  (const uint32_t     value,  const uint32_t  mask,   uint8_t *str);

/* to string */
void        ptp2_log_time                   (uint32_t                        seconds,           uint8_t *str);
//void        ptp2_log_mac                    (const mac_address_t            *mac,               uint8_t *str);
//void        ptp2_log_ipv4                   (const ipv4_address_t           *ipv4,              uint8_t *str);
//void        ptp2_log_ipv6                   (const ipv6_address_t           *ipv6,              uint8_t *str);
//void        ptp2_log_clock_identity         (const ptp2_clock_identity_t    *clock_identity,    uint8_t *str);
//void        ptp2_log_eui48                  (const eui48_t                  *eui48,             uint8_t *str);

/* packets */
//void        ptp2_log_raw_packet             (raw_packet_t                   *raw_packet);
//void        ptp2_log_packet                 (const packet_t                 *packet);
//void        ptp2_log_ethernet_header        (const ethernet_packet_t        *ether_packet);
//void        ptp2_log_arp_packet             (const arp_packet_t             *arp_packet);
//void        ptp2_log_icmp_header            (const icmp_packet_t            *icmp_packet);
//void        ptp2_log_ipv4_header            (const ip_packet_t              *ipv4_packet);
//void        ptp2_log_ipv6_header            (const ip_packet_t              *ipv6_packet);
//void        ptp2_log_udp_header             (const udp_packet_t             *udp_packet);
//void        ptp2_log_ptp2_header            (const ptp2_packet_t            *ptp2_packet);
//void        ptp2_log_ptp2_signaling_header  (const ptp2_signaling_packet_t  *ptp2_signaling_packet);
//void        ptp2_log_ptp2_announce_header   (const ptp2_announce_packet_t   *ptp2_announce_packet);

/* log character streams (like UART or SPI) */
void        ptp2_log_char_stream            (const char *stream, const uint32_t len);

//const char *ptp2_log_packet_type            (const packet_type_t packet_type);
//const char* ptp2_log_node_address_type      (const ptp2_address_type_t type);
const char* ptp2_log_ether_type             (const uint16_t ether_type);
const char *ptp2_log_arp_oper               (const uint16_t oper);
const char *ptp2_log_icmp_type_code         (const uint16_t type, const uint16_t code);
const char *ptp2_log_ipv4_protocol          (const uint8_t ipv4_protocol);
const char *ptp2_log_ipv6_protocol          (const uint8_t ipv6_protocol);
const char* ptp2_log_ip_port                (const uint16_t port);
const char *ptp2_log_message_type           (uint8_t msg_type);
const char *ptp2_log_tlv_type               (uint16_t tlv_type);
//const char *ptp2_log_port_state             (ptp2_port_state_t port_state);
//const char *ptp2_log_time_source            (ptp2_time_source_t time_source);
const char *ptp2_log_clock_accuracy         (uint8_t clock_accuracy);
//const char *ptp2_log_delay_mechanism        (ptp2_delay_mechanism_t delay_mechanism);
//const char *ptp2_log_node_address_state     (ptp2_node_address_state_t state);
#endif

